Gas Data

Reading in Data

We first read in two data sets called “gas” representing several different values having to due with gas stations in the United States. “Gas” has 72798 observations and 32 total variables.

gas <- read.csv("https://ecoleman451.github.io/website/Data%20Visualization/Datasets/POC.csv")
head(gas)
  X site_row_id STATE      county               ADDRESS           CITY   ycoord
1 1  1-3R8J-494    CA Los Angeles       37120 47TH ST E       PALMDALE 34.55584
2 2  1-3R8J-362    WA    Franklin        1212 N 4TH AVE          PASCO 46.23890
3 3  1-3R8J-199    NV      Washoe 99 DAMONTE RANCH PKWY           RENO 39.41961
4 4  1-3R8J-261    UT   Salt Lake         5404 S 4200 W SALT LAKE CITY 40.65107
5 5  1-3R8J-493    CA Los Angeles          1731 E AVE J      LANCASTER 34.68966
6 6  1-3R8J-508    WA      Benton     2707 S QUILLAN ST      KENNEWICK 46.18435
     xcoord                    SITE_DESCRIPTION service_or_fuel diesel
1 -118.0452 Los Angeles-Long Beach-Santa Ana CA            Fuel      Y
2 -119.0950         Kennewick-Pasco-Richland WA            Fuel      N
3 -119.7549                      Reno-Sparks NV            Fuel      Y
4 -112.0101                   Salt Lake City UT            Fuel      N
5 -118.0984 Los Angeles-Long Beach-Santa Ana CA            Fuel      N
6 -119.1739         Kennewick-Pasco-Richland WA            Fuel      Y
  twentyfour_hour_flag car_wash truckstop_flag description PUMP_TECH POC HIFCA
1                    Y        N              Y       URBAN         O   0     0
2                    N        N              N       URBAN         O   0     1
3                    Y        N              N       URBAN         O   0     1
4                    Y        N              N       URBAN         O   0     1
5                    Y        N              N       URBAN         O   0     0
6                    Y        N              N       URBAN         O   0     1
  ZIPnew POCAGE POCGAP ZIPPOC HFG  MSA dist.to.poc cate.poc.density
1  93552     NA     NA      0   1 4480   8.2275601       (-1e-06,1]
2  99301     NA     NA      1   0 6740   0.2788194       (-1e-06,1]
3  89521     NA     NA      1   0 6720   1.3055498            (1,5]
4  84118     NA     NA      0   0 7160   8.2792641       (-1e-06,1]
5  93535     NA     NA      0   1 4480  17.6058504       (-1e-06,1]
6  99337     NA     NA      0   0 6740   8.7976927       (-1e-06,1]
  cate.poc.age cate.poc.age.20 cate.poc.intensity cate.poc.intensity.tot
1     (15,140]        (15,140]            (5,Inf]                (8,Inf]
2     (15,140]        (15,140]            (5,Inf]                (8,Inf]
3       (0,15]          (0,15]              (0,5]                  (0,8]
4     (15,140]        (15,140]              (0,5]                  (0,8]
5    (140,Inf]        (15,140]        (-0.0001,0]            (-0.0001,0]
6     (15,140]        (15,140]            (5,Inf]                (8,Inf]
  MSA_POC MSA_POC.1
1       1         1
2       0         0
3       0         0
4       0         0
5       1         1
6       0         0

Mapping

Simple Leaflet Map

In the following map, each point represents the latitude and longitude values for a specific gas station (represented by “xcoord” and “ycoord” respectively in the “gas” data set). Hovering over each point, you will see the “State”, “County”, “Address”, and “Zip Code” for that specific gas station. The results are below:

 gas_samp <- gas %>% sample_n(500)

# Create a leaflet map
gas_map <- leaflet(data = gas_samp) %>%
  addTiles() %>%
  addMarkers(
    lng = ~xcoord,
    lat = ~ycoord,
    popup = ~paste("State: ", STATE, "<br>",
                   "County: ", county, "<br>",
                   "Address: ", ADDRESS, "<br>",
                   "Zip Code: ", ZIPnew)
  )
# Display the map
gas_map

Leaflet Map

Below, we create a slightly more better leaflet map by using the “radius” and “color” functions. Here, we have the radius fixed at 5, the color fixed at blue, and the markers as circle points instead of location markers. The map is similar to the one above in which each circle point represents a gas station. Hovering over a point will once again give the “State”, “County”, “Address”, and “Zip Code” for that specific gas station.

# Create a leaflet map
gas_map2 <- leaflet(data = gas_samp) %>%
  addTiles() %>%
  setView(lng = mean(gas_samp$xcoord), 
          lat = mean(gas_samp$ycoord), 
          zoom = 13) %>%
  addProviderTiles("Esri.WorldGrayCanvas") %>%
  addCircleMarkers(
    ~xcoord, 
    ~ycoord,
    color = "blue",  # Adjust color as needed
    radius = 5,  # Adjust radius as needed
    stroke = FALSE, 
    fillOpacity = 0.4,
    label = ~paste("State: ", STATE,
                   "County: ", county,
                   "Address: ", ADDRESS,
                   "Zip Code: ", ZIPnew)
  ) %>%
  addLegend(position = "bottomright", 
            colors = "blue",  # Adjust color as needed
            labels = "Gas Station",
            title = "Gas Stations",
            opacity = 0.4)

# Display the map
gas_map2

Best Map

This time, we create a slightly more complex leaflet map by using the “radius” and “color” functions once again. Here, we have the radius dependent on the number of POCs (points of compromise) in a gas station’s respective ZIP Code. This means that the larger a point appears on the map, the more POCs exist in that station’s ZIP Code. The color is dependent on whether or not a gas station provides “Fuel”, “Service Only” or “Both” fuel and services. So, each of those three categories will have a specific color, representing the services provided at a specific gas station. In the data, we do not get any gas stations that provide “Service Only”, so only the categories “Fuel” and “Both” will appear on the map. The map is similar to the previous maps above in which each circle point represents a gas station. Hovering over a point will once again give the “State”, “County”, “Address”, and “Zip Code” for that specific gas station.

# Create a color palette based on service_or_fuel values
service_palette <- colorFactor(palette = "Set1", domain = gas_samp$service_or_fuel)

# Create the leaflet map
gas_map3 <- leaflet(data = gas_samp) %>%
  addTiles() %>%
  addProviderTiles("Esri.WorldGrayCanvas") %>%
  addCircleMarkers(
    ~xcoord, 
    ~ycoord,
    color = ~service_palette(service_or_fuel),  # Use colorFactor
    radius = gas_samp$ZIPPOC * 10,  # Adjust radius as needed
    stroke = FALSE, 
    fillOpacity = 0.4,
    label = ~paste("State: ", STATE, "<br>",
                   "County: ", county, "<br>",
                   "Address: ", ADDRESS, "<br>",
                   "Zip Code: ", ZIPnew)
  ) %>%
  addLegend(position = "bottomright", 
            colors = service_palette(unique(gas_samp$service_or_fuel)),  # Use unique service_or_fuel values
            labels = unique(gas_samp$service_or_fuel),
            title = "Gas Stations",
            opacity = 0.4)

# Display the map
gas_map3

Philly Crime Data

Reading in Data

We first read in two data sets called “philly” representing several different values having to due with crimes that have occurred in the Philadelphia area since 2015. “Philly” has 15520 observations and 18 total variables.

philly <- read.csv("https://ecoleman451.github.io/website/Data%20Visualization/Datasets/PhillyCrimeSince2015.csv")
head(philly)
       dc_key                      race    sex    fatal            date
1 2.02422E+11      Black (Non-Hispanic) Female Nonfatal  3/3/2024 14:49
2 2.02426E+11 Hispanic (Black or White)   Male Nonfatal  3/1/2024 22:18
3 2.02422E+11      Black (Non-Hispanic)   Male    Fatal 2/29/2024 22:59
4 2.02422E+11      Black (Non-Hispanic) Female    Fatal 2/29/2024 22:59
5 2.02419E+11      Black (Non-Hispanic)   Male Nonfatal 2/29/2024 19:30
6 2.02439E+11      Black (Non-Hispanic)   Male    Fatal  2/29/2024 1:53
  has_court_case age       street_name block_number zip_code council_district
1             No  20     N COLORADO ST         2500    19132                5
2             No  58     N FRANKLIN ST         2600    19133                5
3             No  49 MOUNT PLEASANT DR         3700    19121                4
4             No  38 MOUNT PLEASANT DR         3700    19121                4
5             No  19         MASTER ST         5600    19131                4
6             No  31       PULASKI AVE         5500    19144                8
  police_district                       neighborhood house_district
1              22                  Sharswood-Stanton            181
2              26 Northern Liberties-West Kensington            197
3              22                               Park            190
4              22                               Park            190
5              19               Haddington-Overbrook            192
6              39                East Falls-Westside            198
  senate_district                    school_catchment       lng      lat
1               3            Tanner G. Duckrey School -75.16060 39.99166
2               3            John F. Hartranft School -75.14468 39.99152
3               7              James G. Blaine School -75.20027 39.98462
4               7              James G. Blaine School -75.20027 39.98462
5               7 Universal Charter School at Bluford -75.23338 39.97346
6               3                John B. Kelly School -75.17899 40.02939

Convert Date Variable to Date Format and Subset

We want to only have 2023 data in our final subset for our map. Since there doesn’t exist a variable for year, we have to create one using the variable date that already exists in the dataset. Once we have our Year variable, we can easily subset the data to focus only on 2023 data. Now, our new data set has 1666 observations and 19 variables, now including Year.

# Convert date variable to date format
philly$date <- as.Date(philly$date, format = "%m/%d/%Y %H:%M")

# Extract year from date variable
philly$year <- format(philly$date, "%Y")

philly <- subset(philly, year=="2023")
head(philly)
         dc_key                      race  sex    fatal       date
169 2.02439E+11      Black (Non-Hispanic) Male Nonfatal 2023-12-31
170 2.02324E+11 Hispanic (Black or White) Male    Fatal 2023-12-31
171 2.02315E+11      White (Non-Hispanic) Male Nonfatal 2023-12-31
172 2.02314E+11      Black (Non-Hispanic) Male Nonfatal 2023-12-30
173 2.02339E+11 Hispanic (Black or White) Male Nonfatal 2023-12-30
174 2.02316E+11      Black (Non-Hispanic) Male Nonfatal 2023-12-30
    has_court_case age   street_name block_number zip_code council_district
169             No  42   OLD YORK RD         5500    19141                9
170             No  38  E MADISON ST          800    19134                7
171             No  24   COTTMAN AVE         5000    19136                6
172             No  43 W CHELTEN AVE            0    19144                8
173            Yes  29    W TIOGA ST         1700    19140                8
174             No  18   BELMONT AVE         1000    19104                3
    police_district        neighborhood house_district senate_district
169              35               Logan            201               3
170              24    Upper Kensington            180               2
171              15  Wissinoming-Tacony            173               5
172              14          Germantown            198               3
173              39      Nicetown-Tioga            198               3
174              16 Mill Creek-Parkside            190               7
                       school_catchment       lng      lat year
169   Lindley Academy Charter at Birney -75.14464 40.03694 2023
170           Philip H. Sheridan School -75.11514 39.99761 2023
171                Edwin Forrest School -75.03134 40.02534 2023
172                John B. Kelly School -75.17504 40.03520 2023
173 Mastery Charter School at Cleveland -75.15684 40.00685 2023
174          Rudolph Blankenburg School -75.21179 39.97163 2023

Mapping

Now, we can create a leaflet map looking at fatal versus non-fatal crimes that occured in Philadelphia in the year 2023 by using the “color” function once again. The color is dependent on whether or not a crime was labeled as “Fatal” or “Nonfatal”. So, each category will have a specific color, representing the type of crime (fatal vs. nonfatal) that occurred at the time. The map is similar to the previous maps above in which each circle point represents a specific crime. Hovering over a point will give the “Neighborhood”, “Date”, “Race”, and “Sex”, “Age”, and “Street” for that specific crime.

library(leaflet)
library(dplyr)
# Create color palette for fatal and non-fatal crimes
fatal <- "red"
non_fatal <- "blue"

# Create leaflet map
map <- leaflet(philly) %>%
  addTiles() %>%
  addCircleMarkers(
    ~lng, ~lat,
    color = ifelse(philly$fatal == "Fatal", fatal, non_fatal),
    radius = 5,
    label = ~paste("Neighborhood: ", neighborhood,
                   "Date: ", date,
                   "Race: ", race,
                   "Sex: ", sex,
                   "Age: ", age,
                   "Street: ", street_name),
    labelOptions = labelOptions(
      direction = "auto"
    )
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c(fatal, non_fatal),
    labels = c("Fatal", "Non-Fatal"),
    title = "Crime Type"
  ) %>%
  addScaleBar() %>%
  addControl(
    html = "<h4>Philadelphia Crime Locations (2015-2024)</h4>",
    position = "topright"
  )

# Display the map
map

Eyeballing the map, we can see that there seems to be a significant amount more of non-fatal crimes versus fatal crimes. Further analysis would be needed to confirm this, of course.

Philly Crime Data

Reading in Data

We first read in a data set representing several different values having to due with the city of Philadelphia. The data, called “Philly”, contains information on Shootings that have occurred in the area and categorizes them as either “Fatal” or “Non-Fatal”. “Philly” has 15555 observations and 21 total variables.

philly  <- na.omit(st_read("https://pengdsci.github.io/STA553VIZ/w08/PhillyShootings.geojson"))
phillyNeighbor  <- st_read("https://pengdsci.github.io/STA553VIZ/w08/Neighborhoods_Philadelphia.geojson")
head(philly)
Simple feature collection with 6 features and 21 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -75.10962 ymin: 40.01166 xmax: -75.08552 ymax: 40.01489
Geodetic CRS:  WGS 84
  objectid year         dc_key code               date_     time race sex age
1 12728033 2016 201615054780.0  400 2016-06-06 20:00:00 12:15:00    B   M  19
2 12728034 2016 201615117555.0  300 2016-12-03 19:00:00 05:43:00    B   M  38
3 12728035 2018 201815093657.0  400 2018-10-13 20:00:00 21:02:00    B   M  31
4 12728036 2020 202015094989.0  400 2020-12-01 19:00:00 17:12:00    B   M  23
5 12728037 2018 201824100255.0  400 2018-11-08 19:00:00 04:29:00    B   M  27
6 12728038 2019 201924106228.0  400 2019-10-27 20:00:00 04:10:00    W   M  22
     wound officer_involved offender_injured offender_deceased
1     Hand                N                N                 N
2    Chest                N                N                 N
3 Multiple                N                N                 N
4     Hand                N                N                 N
5      Arm                N                N                 N
6 Multiple                N                N                 N
                      location latino   point_x  point_y dist inside outside
1     4600 BLOCK Frankford Ave      0 -75.08552 40.01489   15      0       1
2     4600 BLOCK Frankford Ave      0 -75.08552 40.01489   15      0       1
3     4600 BLOCK Frankford Ave      0 -75.08552 40.01489   15      0       1
4     4600 BLOCK FRANKFORD AVE      0 -75.08552 40.01489   15      0       1
5 900 BLOCK E Hunting Park Ave      0 -75.10962 40.01167   24      0       1
6 900 BLOCK E HUNTING PARK AVE      1 -75.10962 40.01167   24      0       1
  fatal                   geometry
1     0 POINT (-75.08552 40.01489)
2     0 POINT (-75.08552 40.01489)
3     1 POINT (-75.08552 40.01489)
4     0 POINT (-75.08552 40.01489)
5     0 POINT (-75.10962 40.01167)
6     0 POINT (-75.10962 40.01167)

Mapping

Now, we can create a leaflet map looking at fatal versus non-fatal crimes that occured in Philadelphia by using the “color” function once again. The color is dependent on whether or not a crime was labeled as “Fatal” or “Nonfatal”. So, each category will have a specific color, representing the type of crime (fatal vs. nonfatal) that occurred at the time. Each crime location is represented with a circle marker. Hovering over a point will display information for “Object ID”, “Year”, “Race”, “Sex”, “Age”, “Wound”, and “Location” for each of the crime points.

# Load required libraries
library(leaflet)
library(sf)

# Convert 'philly' data to sf object
philly_sf <- st_as_sf(philly, coords = c("point_x", "point_y"), crs = 4326)

# Define color palette for fatal and non-fatal crimes
fatal_color <- "red"
non_fatal_color <- "gold"

# Create leaflet map
map <- leaflet() %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addPolygons(data = phillyNeighbor,
              color = 'skyblue',
              weight = 1)  %>%
  addCircleMarkers(data = philly_sf,
                   ~point_x, ~point_y,
                   color = ifelse(philly$fatal == 1, fatal_color, non_fatal_color),
                   radius = 5,
                   popup = ~paste("Object ID: ", objectid,
                                  "<br>Year: ", year,
                                  "<br>Race: ", race,
                                  "<br>Sex: ", sex,
                                  "<br>Age: ", age,
                                  "<br>Wound: ", wound,
                                  "<br>Location: ", location),
                   labelOptions = labelOptions(
                     direction = "auto"
                   )
  ) %>%
  addLegend(
    position = "bottomright",
    colors = c("red", "gold"),
    labels = c("Fatal", "Non-Fatal"),
    title = "Crime Type"
  ) %>%
  addScaleBar() %>%
  addControl(
    html = "<h4>Philadelphia Crime Locations (2015-2024)</h4>",
    position = "topright"
  ) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  setView(lng = -75.1527, lat = 39.9707, zoom = 11)

# Display the map
map
LS0tDQp0aXRsZTogIk1hcHBpbmciDQphdXRob3I6ICJFZHdhcmQgQ29sZW1hbiINCmRhdGU6ICJXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQplZGl0b3Jfb3B0aW9uczoNCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOmxpZ2h0Z3JheTsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQogICAgZm9udC1mYW1pbHk6IEFyaWFsLCBIZWx2ZXRpY2EsIHNhbnMtc2VyaWY7DQogICAgY29sb3I6ICM3ODBjMGM7DQp9DQoNCmgxLnRpdGxlIHsNCiAgZm9udC1zaXplOiAyNHB4Ow0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KICBmb250LWZhbWlseTogQXJpYWwsIEhlbHZldGljYSwgc2Fucy1zZXJpZjsNCiAgZm9udC12YXJpYW50LWNhcHM6IG5vcm1hbDsNCn0NCmg0LmF1dGhvciB7IA0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyANCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDIgeyANCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyANCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgew0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQoNCi8qIFRhYiBmZWF0dXJlcyAqLw0KLm5hdj5saT5hIHsNCiAgICBwb3NpdGlvbjogcmVsYXRpdmU7DQogICAgZGlzcGxheTogYmxvY2s7DQogICAgcGFkZGluZzogMnB4IDE1cHg7DQogICAgY29sb3I6ICM5OTAwMDA7DQp9DQoubmF2LXBpbGxzPmxpLmFjdGl2ZT5hLCAubmF2LXBpbGxzPmxpLmFjdGl2ZT5hOmhvdmVyLCAubmF2LXBpbGxzPmxpLmFjdGl2ZT5hOmZvY3VzIHsNCiAgICBjb2xvcjogI2ZmZmZmZjsNCiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjOTkwMDAwOw0KfQ0KLyoNCm5hdi1waWxscz5saTpudGgtY2hpbGQoMikgew0KICAgIGJhY2tncm91bmQ6IGdyZWVuOw0KIH0NCiAqLw0KPC9zdHlsZT4NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIGNvbW1lbnQ9TkF9DQpvcHRpb25zKHJlcG9zID0gbGlzdChDUkFOPSJodHRwOi8vY3Jhbi5yc3R1ZGlvLmNvbS8iKSkNCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQogICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgiY293cGxvdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJjb3dwbG90IikNCiAgIGxpYnJhcnkoY293cGxvdCkNCn0NCmlmICghcmVxdWlyZSgibGF0ZXgyZXhwIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImxhdGV4MmV4cCIpDQogICBsaWJyYXJ5KGxhdGV4MmV4cCkNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQogICBsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiZ2FwbWluZGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImdhcG1pbmRlciIpDQogICBsaWJyYXJ5KGdhcG1pbmRlcikNCn0NCmlmICghcmVxdWlyZSgicG5nIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbmciKSAgICANCiAgICBsaWJyYXJ5KCJwbmciKQ0KfQ0KaWYgKCFyZXF1aXJlKCJSQ3VybCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiUkN1cmwiKSAgICANCiAgICBsaWJyYXJ5KCJSQ3VybCIpDQp9DQppZiAoIXJlcXVpcmUoImNvbG91cnBpY2tlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiY29sb3VycGlja2VyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImNvbG91cnBpY2tlciIpDQp9DQppZiAoIXJlcXVpcmUoImdnYW5pbWF0ZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dhbmltYXRlIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdnYW5pbWF0ZSIpDQp9DQppZiAoIXJlcXVpcmUoImdpZnNraSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2lmc2tpIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdpZnNraSIpDQp9DQppZiAoIXJlcXVpcmUoIm1hZ2ljayIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1hZ2ljayIpDQp9DQppZiAoIXJlcXVpcmUoImdyRGV2aWNlcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ3JEZXZpY2VzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdyRGV2aWNlcyIpDQp9DQppZiAoIXJlcXVpcmUoImpwZWciKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImpwZWciKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgianBlZyIpDQp9DQppZiAoIXJlcXVpcmUoImdncmlkZ2VzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ3JpZGdlcyIpDQp9DQppZiAoIXJlcXVpcmUoInBseXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBseXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGx5ciIpDQp9DQppZiAoIXJlcXVpcmUoImdnaXJhcGgiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdnaXJhcGgiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dpcmFwaCIpDQp9DQppZiAoIXJlcXVpcmUoImhpZ2hjaGFydGVyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJoaWdoY2hhcnRlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJoaWdoY2hhcnRlciIpDQp9DQppZiAoIXJlcXVpcmUoImZvcmVjYXN0IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJmb3JlY2FzdCIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJmb3JlY2FzdCIpDQp9DQppZiAoIXJlcXVpcmUoImxlYWZsZXQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibGVhZmxldCIpDQp9DQppZiAoIXJlcXVpcmUoInNmIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzZiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJzZiIpDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSkNCmBgYA0KDQojIEdhcyBEYXRhDQojIyBSZWFkaW5nIGluIERhdGENCiAgV2UgZmlyc3QgcmVhZCBpbiB0d28gZGF0YSBzZXRzIGNhbGxlZCAiZ2FzIiByZXByZXNlbnRpbmcgc2V2ZXJhbCBkaWZmZXJlbnQgdmFsdWVzIGhhdmluZyB0byBkdWUgd2l0aCBnYXMgc3RhdGlvbnMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMuICJHYXMiIGhhcyA3Mjc5OCBvYnNlcnZhdGlvbnMgYW5kIDMyIHRvdGFsIHZhcmlhYmxlcy4NCmBgYHtyLCBjb21tZW50PU5BfQ0KZ2FzIDwtIHJlYWQuY3N2KCJodHRwczovL2Vjb2xlbWFuNDUxLmdpdGh1Yi5pby93ZWJzaXRlL0RhdGElMjBWaXN1YWxpemF0aW9uL0RhdGFzZXRzL1BPQy5jc3YiKQ0KaGVhZChnYXMpDQpgYGANCg0KIyBNYXBwaW5nDQojIyBTaW1wbGUgTGVhZmxldCBNYXANCiAgSW4gdGhlIGZvbGxvd2luZyBtYXAsIGVhY2ggcG9pbnQgcmVwcmVzZW50cyB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YWx1ZXMgZm9yIGEgc3BlY2lmaWMgZ2FzIHN0YXRpb24gKHJlcHJlc2VudGVkIGJ5ICJ4Y29vcmQiIGFuZCAieWNvb3JkIiByZXNwZWN0aXZlbHkgaW4gdGhlICJnYXMiIGRhdGEgc2V0KS4gSG92ZXJpbmcgb3ZlciBlYWNoIHBvaW50LCB5b3Ugd2lsbCBzZWUgdGhlICJTdGF0ZSIsICJDb3VudHkiLCAiQWRkcmVzcyIsIGFuZCAiWmlwIENvZGUiIGZvciB0aGF0IHNwZWNpZmljIGdhcyBzdGF0aW9uLiBUaGUgcmVzdWx0cyBhcmUgYmVsb3c6DQpgYGB7ciwgY29tbWVudD1OQX0NCiBnYXNfc2FtcCA8LSBnYXMgJT4lIHNhbXBsZV9uKDUwMCkNCg0KIyBDcmVhdGUgYSBsZWFmbGV0IG1hcA0KZ2FzX21hcCA8LSBsZWFmbGV0KGRhdGEgPSBnYXNfc2FtcCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZE1hcmtlcnMoDQogICAgbG5nID0gfnhjb29yZCwNCiAgICBsYXQgPSB+eWNvb3JkLA0KICAgIHBvcHVwID0gfnBhc3RlKCJTdGF0ZTogIiwgU1RBVEUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQ291bnR5OiAiLCBjb3VudHksICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQWRkcmVzczogIiwgQUREUkVTUywgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICJaaXAgQ29kZTogIiwgWklQbmV3KQ0KICApDQojIERpc3BsYXkgdGhlIG1hcA0KZ2FzX21hcA0KYGBgDQoNCiMjIExlYWZsZXQgTWFwDQogIEJlbG93LCB3ZSBjcmVhdGUgYSBzbGlnaHRseSBtb3JlIGJldHRlciBsZWFmbGV0IG1hcCBieSB1c2luZyB0aGUgInJhZGl1cyIgYW5kICJjb2xvciIgZnVuY3Rpb25zLiBIZXJlLCB3ZSBoYXZlIHRoZSByYWRpdXMgZml4ZWQgYXQgNSwgdGhlIGNvbG9yIGZpeGVkIGF0IGJsdWUsIGFuZCB0aGUgbWFya2VycyBhcyBjaXJjbGUgcG9pbnRzIGluc3RlYWQgb2YgbG9jYXRpb24gbWFya2Vycy4gVGhlIG1hcCBpcyBzaW1pbGFyIHRvIHRoZSBvbmUgYWJvdmUgaW4gd2hpY2ggZWFjaCBjaXJjbGUgcG9pbnQgcmVwcmVzZW50cyBhIGdhcyBzdGF0aW9uLiBIb3ZlcmluZyBvdmVyIGEgcG9pbnQgd2lsbCBvbmNlIGFnYWluIGdpdmUgdGhlICJTdGF0ZSIsICJDb3VudHkiLCAiQWRkcmVzcyIsIGFuZCAiWmlwIENvZGUiIGZvciB0aGF0IHNwZWNpZmljIGdhcyBzdGF0aW9uLg0KYGBge3IsIGNvbW1lbnQ9TkF9DQojIENyZWF0ZSBhIGxlYWZsZXQgbWFwDQpnYXNfbWFwMiA8LSBsZWFmbGV0KGRhdGEgPSBnYXNfc2FtcCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIHNldFZpZXcobG5nID0gbWVhbihnYXNfc2FtcCR4Y29vcmQpLCANCiAgICAgICAgICBsYXQgPSBtZWFuKGdhc19zYW1wJHljb29yZCksIA0KICAgICAgICAgIHpvb20gPSAxMykgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuV29ybGRHcmF5Q2FudmFzIikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgfnhjb29yZCwgDQogICAgfnljb29yZCwNCiAgICBjb2xvciA9ICJibHVlIiwgICMgQWRqdXN0IGNvbG9yIGFzIG5lZWRlZA0KICAgIHJhZGl1cyA9IDUsICAjIEFkanVzdCByYWRpdXMgYXMgbmVlZGVkDQogICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgIGZpbGxPcGFjaXR5ID0gMC40LA0KICAgIGxhYmVsID0gfnBhc3RlKCJTdGF0ZTogIiwgU1RBVEUsDQogICAgICAgICAgICAgICAgICAgIkNvdW50eTogIiwgY291bnR5LA0KICAgICAgICAgICAgICAgICAgICJBZGRyZXNzOiAiLCBBRERSRVNTLA0KICAgICAgICAgICAgICAgICAgICJaaXAgQ29kZTogIiwgWklQbmV3KQ0KICApICU+JQ0KICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgIGNvbG9ycyA9ICJibHVlIiwgICMgQWRqdXN0IGNvbG9yIGFzIG5lZWRlZA0KICAgICAgICAgICAgbGFiZWxzID0gIkdhcyBTdGF0aW9uIiwNCiAgICAgICAgICAgIHRpdGxlID0gIkdhcyBTdGF0aW9ucyIsDQogICAgICAgICAgICBvcGFjaXR5ID0gMC40KQ0KDQojIERpc3BsYXkgdGhlIG1hcA0KZ2FzX21hcDINCmBgYA0KDQojIyBCZXN0IE1hcA0KIFRoaXMgdGltZSwgd2UgY3JlYXRlIGEgc2xpZ2h0bHkgbW9yZSBjb21wbGV4IGxlYWZsZXQgbWFwIGJ5IHVzaW5nIHRoZSAicmFkaXVzIiBhbmQgImNvbG9yIiBmdW5jdGlvbnMgb25jZSBhZ2Fpbi4gSGVyZSwgd2UgaGF2ZSB0aGUgcmFkaXVzIGRlcGVuZGVudCBvbiB0aGUgbnVtYmVyIG9mIFBPQ3MgKHBvaW50cyBvZiBjb21wcm9taXNlKSBpbiBhIGdhcyBzdGF0aW9uJ3MgcmVzcGVjdGl2ZSBaSVAgQ29kZS4gVGhpcyBtZWFucyB0aGF0IHRoZSBsYXJnZXIgYSBwb2ludCBhcHBlYXJzIG9uIHRoZSBtYXAsIHRoZSBtb3JlIFBPQ3MgZXhpc3QgaW4gdGhhdCBzdGF0aW9uJ3MgWklQIENvZGUuIFRoZSBjb2xvciBpcyBkZXBlbmRlbnQgb24gd2hldGhlciBvciBub3QgYSBnYXMgc3RhdGlvbiBwcm92aWRlcyAiRnVlbCIsICJTZXJ2aWNlIE9ubHkiIG9yICJCb3RoIiBmdWVsIGFuZCBzZXJ2aWNlcy4gU28sIGVhY2ggb2YgdGhvc2UgdGhyZWUgY2F0ZWdvcmllcyB3aWxsIGhhdmUgYSBzcGVjaWZpYyBjb2xvciwgcmVwcmVzZW50aW5nIHRoZSBzZXJ2aWNlcyBwcm92aWRlZCBhdCBhIHNwZWNpZmljIGdhcyBzdGF0aW9uLiBJbiB0aGUgZGF0YSwgd2UgZG8gbm90IGdldCBhbnkgZ2FzIHN0YXRpb25zIHRoYXQgcHJvdmlkZSAiU2VydmljZSBPbmx5Iiwgc28gb25seSB0aGUgY2F0ZWdvcmllcyAiRnVlbCIgYW5kICJCb3RoIiB3aWxsIGFwcGVhciBvbiB0aGUgbWFwLiBUaGUgbWFwIGlzIHNpbWlsYXIgdG8gdGhlIHByZXZpb3VzIG1hcHMgYWJvdmUgaW4gd2hpY2ggZWFjaCBjaXJjbGUgcG9pbnQgcmVwcmVzZW50cyBhIGdhcyBzdGF0aW9uLiBIb3ZlcmluZyBvdmVyIGEgcG9pbnQgd2lsbCBvbmNlIGFnYWluIGdpdmUgdGhlICJTdGF0ZSIsICJDb3VudHkiLCAiQWRkcmVzcyIsIGFuZCAiWmlwIENvZGUiIGZvciB0aGF0IHNwZWNpZmljIGdhcyBzdGF0aW9uLg0KYGBge3IsIGNvbW1lbnQ9TkF9DQojIENyZWF0ZSBhIGNvbG9yIHBhbGV0dGUgYmFzZWQgb24gc2VydmljZV9vcl9mdWVsIHZhbHVlcw0Kc2VydmljZV9wYWxldHRlIDwtIGNvbG9yRmFjdG9yKHBhbGV0dGUgPSAiU2V0MSIsIGRvbWFpbiA9IGdhc19zYW1wJHNlcnZpY2Vfb3JfZnVlbCkNCg0KIyBDcmVhdGUgdGhlIGxlYWZsZXQgbWFwDQpnYXNfbWFwMyA8LSBsZWFmbGV0KGRhdGEgPSBnYXNfc2FtcCkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMoIkVzcmkuV29ybGRHcmF5Q2FudmFzIikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgfnhjb29yZCwgDQogICAgfnljb29yZCwNCiAgICBjb2xvciA9IH5zZXJ2aWNlX3BhbGV0dGUoc2VydmljZV9vcl9mdWVsKSwgICMgVXNlIGNvbG9yRmFjdG9yDQogICAgcmFkaXVzID0gZ2FzX3NhbXAkWklQUE9DICogMTAsICAjIEFkanVzdCByYWRpdXMgYXMgbmVlZGVkDQogICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgIGZpbGxPcGFjaXR5ID0gMC40LA0KICAgIGxhYmVsID0gfnBhc3RlKCJTdGF0ZTogIiwgU1RBVEUsICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQ291bnR5OiAiLCBjb3VudHksICI8YnI+IiwNCiAgICAgICAgICAgICAgICAgICAiQWRkcmVzczogIiwgQUREUkVTUywgIjxicj4iLA0KICAgICAgICAgICAgICAgICAgICJaaXAgQ29kZTogIiwgWklQbmV3KQ0KICApICU+JQ0KICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgIGNvbG9ycyA9IHNlcnZpY2VfcGFsZXR0ZSh1bmlxdWUoZ2FzX3NhbXAkc2VydmljZV9vcl9mdWVsKSksICAjIFVzZSB1bmlxdWUgc2VydmljZV9vcl9mdWVsIHZhbHVlcw0KICAgICAgICAgICAgbGFiZWxzID0gdW5pcXVlKGdhc19zYW1wJHNlcnZpY2Vfb3JfZnVlbCksDQogICAgICAgICAgICB0aXRsZSA9ICJHYXMgU3RhdGlvbnMiLA0KICAgICAgICAgICAgb3BhY2l0eSA9IDAuNCkNCg0KIyBEaXNwbGF5IHRoZSBtYXANCmdhc19tYXAzDQpgYGANCg0KDQojIFBoaWxseSBDcmltZSBEYXRhDQojIyBSZWFkaW5nIGluIERhdGENCiAgV2UgZmlyc3QgcmVhZCBpbiB0d28gZGF0YSBzZXRzIGNhbGxlZCAicGhpbGx5IiByZXByZXNlbnRpbmcgc2V2ZXJhbCBkaWZmZXJlbnQgdmFsdWVzIGhhdmluZyB0byBkdWUgd2l0aCBjcmltZXMgdGhhdCBoYXZlIG9jY3VycmVkIGluIHRoZSBQaGlsYWRlbHBoaWEgYXJlYSBzaW5jZSAyMDE1LiAiUGhpbGx5IiBoYXMgMTU1MjAgb2JzZXJ2YXRpb25zIGFuZCAxOCB0b3RhbCB2YXJpYWJsZXMuDQpgYGB7ciwgY29tbWVudD1OQX0NCnBoaWxseSA8LSByZWFkLmNzdigiaHR0cHM6Ly9lY29sZW1hbjQ1MS5naXRodWIuaW8vd2Vic2l0ZS9EYXRhJTIwVmlzdWFsaXphdGlvbi9EYXRhc2V0cy9QaGlsbHlDcmltZVNpbmNlMjAxNS5jc3YiKQ0KaGVhZChwaGlsbHkpDQpgYGANCiMjIENvbnZlcnQgRGF0ZSBWYXJpYWJsZSB0byBEYXRlIEZvcm1hdCBhbmQgU3Vic2V0DQogIFdlIHdhbnQgdG8gb25seSBoYXZlIDIwMjMgZGF0YSBpbiBvdXIgZmluYWwgc3Vic2V0IGZvciBvdXIgbWFwLiBTaW5jZSB0aGVyZSBkb2Vzbid0IGV4aXN0IGEgdmFyaWFibGUgZm9yIHllYXIsIHdlIGhhdmUgdG8gY3JlYXRlIG9uZSB1c2luZyB0aGUgdmFyaWFibGUgZGF0ZSB0aGF0IGFscmVhZHkgZXhpc3RzIGluIHRoZSBkYXRhc2V0LiBPbmNlIHdlIGhhdmUgb3VyIFllYXIgdmFyaWFibGUsIHdlIGNhbiBlYXNpbHkgc3Vic2V0IHRoZSBkYXRhIHRvIGZvY3VzIG9ubHkgb24gMjAyMyBkYXRhLiBOb3csIG91ciBuZXcgZGF0YSBzZXQgaGFzIDE2NjYgb2JzZXJ2YXRpb25zIGFuZCAxOSB2YXJpYWJsZXMsIG5vdyBpbmNsdWRpbmcgWWVhci4NCmBgYHtyLCBjb21tZW50ID0gTkF9DQojIENvbnZlcnQgZGF0ZSB2YXJpYWJsZSB0byBkYXRlIGZvcm1hdA0KcGhpbGx5JGRhdGUgPC0gYXMuRGF0ZShwaGlsbHkkZGF0ZSwgZm9ybWF0ID0gIiVtLyVkLyVZICVIOiVNIikNCg0KIyBFeHRyYWN0IHllYXIgZnJvbSBkYXRlIHZhcmlhYmxlDQpwaGlsbHkkeWVhciA8LSBmb3JtYXQocGhpbGx5JGRhdGUsICIlWSIpDQoNCnBoaWxseSA8LSBzdWJzZXQocGhpbGx5LCB5ZWFyPT0iMjAyMyIpDQpoZWFkKHBoaWxseSkNCmBgYA0KDQojIyBNYXBwaW5nDQogIE5vdywgd2UgY2FuIGNyZWF0ZSBhIGxlYWZsZXQgbWFwIGxvb2tpbmcgYXQgZmF0YWwgdmVyc3VzIG5vbi1mYXRhbCBjcmltZXMgdGhhdCBvY2N1cmVkIGluIFBoaWxhZGVscGhpYSBpbiB0aGUgeWVhciAyMDIzIGJ5IHVzaW5nIHRoZSAiY29sb3IiIGZ1bmN0aW9uIG9uY2UgYWdhaW4uIFRoZSBjb2xvciBpcyBkZXBlbmRlbnQgb24gd2hldGhlciBvciBub3QgYSBjcmltZSB3YXMgbGFiZWxlZCBhcyAiRmF0YWwiIG9yICJOb25mYXRhbCIuIFNvLCBlYWNoIGNhdGVnb3J5IHdpbGwgaGF2ZSBhIHNwZWNpZmljIGNvbG9yLCByZXByZXNlbnRpbmcgdGhlIHR5cGUgb2YgY3JpbWUgKGZhdGFsIHZzLiBub25mYXRhbCkgdGhhdCBvY2N1cnJlZCBhdCB0aGUgdGltZS4gVGhlIG1hcCBpcyBzaW1pbGFyIHRvIHRoZSBwcmV2aW91cyBtYXBzIGFib3ZlIGluIHdoaWNoIGVhY2ggY2lyY2xlIHBvaW50IHJlcHJlc2VudHMgYSBzcGVjaWZpYyBjcmltZS4gSG92ZXJpbmcgb3ZlciBhIHBvaW50IHdpbGwgZ2l2ZSB0aGUgIk5laWdoYm9yaG9vZCIsICJEYXRlIiwgIlJhY2UiLCBhbmQgIlNleCIsICJBZ2UiLCBhbmQgIlN0cmVldCIgZm9yIHRoYXQgc3BlY2lmaWMgY3JpbWUuDQpgYGB7ciwgY29tbWVudD1OQX0NCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkoZHBseXIpDQojIENyZWF0ZSBjb2xvciBwYWxldHRlIGZvciBmYXRhbCBhbmQgbm9uLWZhdGFsIGNyaW1lcw0KZmF0YWwgPC0gInJlZCINCm5vbl9mYXRhbCA8LSAiYmx1ZSINCg0KIyBDcmVhdGUgbGVhZmxldCBtYXANCm1hcCA8LSBsZWFmbGV0KHBoaWxseSkgJT4lDQogIGFkZFRpbGVzKCkgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoDQogICAgfmxuZywgfmxhdCwNCiAgICBjb2xvciA9IGlmZWxzZShwaGlsbHkkZmF0YWwgPT0gIkZhdGFsIiwgZmF0YWwsIG5vbl9mYXRhbCksDQogICAgcmFkaXVzID0gNSwNCiAgICBsYWJlbCA9IH5wYXN0ZSgiTmVpZ2hib3Job29kOiAiLCBuZWlnaGJvcmhvb2QsDQogICAgICAgICAgICAgICAgICAgIkRhdGU6ICIsIGRhdGUsDQogICAgICAgICAgICAgICAgICAgIlJhY2U6ICIsIHJhY2UsDQogICAgICAgICAgICAgICAgICAgIlNleDogIiwgc2V4LA0KICAgICAgICAgICAgICAgICAgICJBZ2U6ICIsIGFnZSwNCiAgICAgICAgICAgICAgICAgICAiU3RyZWV0OiAiLCBzdHJlZXRfbmFtZSksDQogICAgbGFiZWxPcHRpb25zID0gbGFiZWxPcHRpb25zKA0KICAgICAgZGlyZWN0aW9uID0gImF1dG8iDQogICAgKQ0KICApICU+JQ0KICBhZGRMZWdlbmQoDQogICAgcG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLA0KICAgIGNvbG9ycyA9IGMoZmF0YWwsIG5vbl9mYXRhbCksDQogICAgbGFiZWxzID0gYygiRmF0YWwiLCAiTm9uLUZhdGFsIiksDQogICAgdGl0bGUgPSAiQ3JpbWUgVHlwZSINCiAgKSAlPiUNCiAgYWRkU2NhbGVCYXIoKSAlPiUNCiAgYWRkQ29udHJvbCgNCiAgICBodG1sID0gIjxoND5QaGlsYWRlbHBoaWEgQ3JpbWUgTG9jYXRpb25zICgyMDE1LTIwMjQpPC9oND4iLA0KICAgIHBvc2l0aW9uID0gInRvcHJpZ2h0Ig0KICApDQoNCiMgRGlzcGxheSB0aGUgbWFwDQptYXANCmBgYA0KICBFeWViYWxsaW5nIHRoZSBtYXAsIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBzZWVtcyB0byBiZSBhIHNpZ25pZmljYW50IGFtb3VudCBtb3JlIG9mIG5vbi1mYXRhbCBjcmltZXMgdmVyc3VzIGZhdGFsIGNyaW1lcy4gRnVydGhlciBhbmFseXNpcyB3b3VsZCBiZSBuZWVkZWQgdG8gY29uZmlybSB0aGlzLCBvZiBjb3Vyc2UuDQoNCg0KIyBQaGlsbHkgQ3JpbWUgRGF0YQ0KIyMgUmVhZGluZyBpbiBEYXRhDQogIFdlIGZpcnN0IHJlYWQgaW4gYSBkYXRhIHNldCByZXByZXNlbnRpbmcgc2V2ZXJhbCBkaWZmZXJlbnQgdmFsdWVzIGhhdmluZyB0byBkdWUgd2l0aCB0aGUgY2l0eSBvZiBQaGlsYWRlbHBoaWEuIFRoZSBkYXRhLCBjYWxsZWQgIlBoaWxseSIsIGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIFNob290aW5ncyB0aGF0IGhhdmUgb2NjdXJyZWQgaW4gdGhlIGFyZWEgYW5kIGNhdGVnb3JpemVzIHRoZW0gYXMgZWl0aGVyICJGYXRhbCIgb3IgIk5vbi1GYXRhbCIuICJQaGlsbHkiIGhhcyAxNTU1NSBvYnNlcnZhdGlvbnMgYW5kIDIxIHRvdGFsIHZhcmlhYmxlcy4NCmBgYHtyLCBjb21tZW50PU5BLCByZXN1bHRzPUZBTFNFfQ0KcGhpbGx5ICA8LSBuYS5vbWl0KHN0X3JlYWQoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvUGhpbGx5U2hvb3RpbmdzLmdlb2pzb24iKSkNCnBoaWxseU5laWdoYm9yICA8LSBzdF9yZWFkKCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhLmdlb2pzb24iKQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChwaGlsbHkpDQpgYGANCg0KDQojIyBNYXBwaW5nDQogIE5vdywgd2UgY2FuIGNyZWF0ZSBhIGxlYWZsZXQgbWFwIGxvb2tpbmcgYXQgZmF0YWwgdmVyc3VzIG5vbi1mYXRhbCBjcmltZXMgdGhhdCBvY2N1cmVkIGluIFBoaWxhZGVscGhpYSBieSB1c2luZyB0aGUgImNvbG9yIiBmdW5jdGlvbiBvbmNlIGFnYWluLiBUaGUgY29sb3IgaXMgZGVwZW5kZW50IG9uIHdoZXRoZXIgb3Igbm90IGEgY3JpbWUgd2FzIGxhYmVsZWQgYXMgIkZhdGFsIiBvciAiTm9uZmF0YWwiLiBTbywgZWFjaCBjYXRlZ29yeSB3aWxsIGhhdmUgYSBzcGVjaWZpYyBjb2xvciwgcmVwcmVzZW50aW5nIHRoZSB0eXBlIG9mIGNyaW1lIChmYXRhbCB2cy4gbm9uZmF0YWwpIHRoYXQgb2NjdXJyZWQgYXQgdGhlIHRpbWUuIEVhY2ggY3JpbWUgbG9jYXRpb24gaXMgcmVwcmVzZW50ZWQgd2l0aCBhIGNpcmNsZSBtYXJrZXIuIEhvdmVyaW5nIG92ZXIgYSBwb2ludCB3aWxsIGRpc3BsYXkgaW5mb3JtYXRpb24gZm9yICJPYmplY3QgSUQiLCAiWWVhciIsICJSYWNlIiwgIlNleCIsICJBZ2UiLCAiV291bmQiLCBhbmQgIkxvY2F0aW9uIiBmb3IgZWFjaCBvZiB0aGUgY3JpbWUgcG9pbnRzLg0KYGBge3J9DQojIExvYWQgcmVxdWlyZWQgbGlicmFyaWVzDQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHNmKQ0KDQojIENvbnZlcnQgJ3BoaWxseScgZGF0YSB0byBzZiBvYmplY3QNCnBoaWxseV9zZiA8LSBzdF9hc19zZihwaGlsbHksIGNvb3JkcyA9IGMoInBvaW50X3giLCAicG9pbnRfeSIpLCBjcnMgPSA0MzI2KQ0KDQojIERlZmluZSBjb2xvciBwYWxldHRlIGZvciBmYXRhbCBhbmQgbm9uLWZhdGFsIGNyaW1lcw0KZmF0YWxfY29sb3IgPC0gInJlZCINCm5vbl9mYXRhbF9jb2xvciA8LSAiZ29sZCINCg0KIyBDcmVhdGUgbGVhZmxldCBtYXANCm1hcCA8LSBsZWFmbGV0KCkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJEVzcmkuV29ybGRHcmF5Q2FudmFzKSAlPiUNCiAgYWRkUG9seWdvbnMoZGF0YSA9IHBoaWxseU5laWdoYm9yLA0KICAgICAgICAgICAgICBjb2xvciA9ICdza3libHVlJywNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMSkgICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBwaGlsbHlfc2YsDQogICAgICAgICAgICAgICAgICAgfnBvaW50X3gsIH5wb2ludF95LA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gaWZlbHNlKHBoaWxseSRmYXRhbCA9PSAxLCBmYXRhbF9jb2xvciwgbm9uX2ZhdGFsX2NvbG9yKSwNCiAgICAgICAgICAgICAgICAgICByYWRpdXMgPSA1LA0KICAgICAgICAgICAgICAgICAgIHBvcHVwID0gfnBhc3RlKCJPYmplY3QgSUQ6ICIsIG9iamVjdGlkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+WWVhcjogIiwgeWVhciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPlJhY2U6ICIsIHJhY2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5TZXg6ICIsIHNleCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiPGJyPkFnZTogIiwgYWdlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+V291bmQ6ICIsIHdvdW5kLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+TG9jYXRpb246ICIsIGxvY2F0aW9uKSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbE9wdGlvbnMgPSBsYWJlbE9wdGlvbnMoDQogICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAiYXV0byINCiAgICAgICAgICAgICAgICAgICApDQogICkgJT4lDQogIGFkZExlZ2VuZCgNCiAgICBwb3NpdGlvbiA9ICJib3R0b21yaWdodCIsDQogICAgY29sb3JzID0gYygicmVkIiwgImdvbGQiKSwNCiAgICBsYWJlbHMgPSBjKCJGYXRhbCIsICJOb24tRmF0YWwiKSwNCiAgICB0aXRsZSA9ICJDcmltZSBUeXBlIg0KICApICU+JQ0KICBhZGRTY2FsZUJhcigpICU+JQ0KICBhZGRDb250cm9sKA0KICAgIGh0bWwgPSAiPGg0PlBoaWxhZGVscGhpYSBDcmltZSBMb2NhdGlvbnMgKDIwMTUtMjAyNCk8L2g0PiIsDQogICAgcG9zaXRpb24gPSAidG9wcmlnaHQiDQogICkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJEVzcmkuV29ybGRHcmF5Q2FudmFzKSAlPiUNCiAgc2V0VmlldyhsbmcgPSAtNzUuMTUyNywgbGF0ID0gMzkuOTcwNywgem9vbSA9IDExKQ0KDQojIERpc3BsYXkgdGhlIG1hcA0KbWFwDQpgYGANCg0K